package com.ease.actionscript.mapping.mapper;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.ApplicationContext;

import xjavadoc.SourceSet;
import xjavadoc.XClass;
import xjavadoc.XJavaDoc;
import xjavadoc.filesystem.FileSourceSet;

import com.ease.actionscript.Remoting;
import com.ease.actionscript.mapping.util.ActionScriptTypeConverter;
import com.ease.actionscript.mapping.vo.ActionScriptMappingContext;
import com.ease.actionscript.mapping.vo.ActionScriptTypeDefinition;
import com.ease.common.util.Detect;
import com.ease.common.util.OutputUtil;
import com.ease.document.freemarker.FreeMarkerUtil;

/**
 * 
 * @author nathanleewei
 * 
 */
public abstract class AbstractActionScriptTypeMapper extends Detect {

	public static final String CLASS_DEFAULTSERVICEPROXY = "com.obas.flex.common.proxy.DefaultServiceProxy";

	public static final String CLASS_ENUM = "com.ease.lang.Enum";

	public static final String CLASS_ENUMCONSTANTS = "com.ease.lang.EnumConstants";

	protected String projectName;

	// protected static final String SOURCE_FOLDER = "/src";

	// protected String[] packagePrefixs;

	protected ActionScriptMappingContext actionScriptMappingContext;

	protected ApplicationContext applicationContext;

	protected String destinationPath;

	protected String destinationFileName;

	public static Modifier modifier = new Modifier();

	public static String[] ignoreMethodNames = null;

	static {
		Method[] methods = Object.class.getMethods();
		List<String> methodNames = new ArrayList<String>();
		for (Method method : methods) {
			methodNames.add(method.getName());
		}
		ignoreMethodNames = methodNames.toArray(new String[methodNames.size()]);
	}

	public boolean isCompliant() {
		return true;
	}

	public abstract void prepare();

	// destinationPath = "";
	// destinationFileName = "";

	protected void optimize() {

	}

	public static String buildDestinationProjectName(String projectName) {
		return projectName.replaceAll(".java.api", ".java").replaceAll(".impl", "").replaceAll(".java", ".flex.remoting");
	}

	public static String absoluteSourcePath(String workspacePath, String projectName) {
		String srcPath = workspacePath + "/" + projectName + "/src";
		// srcPath = StringUtils.replace(srcPath, "\\", "/");
		// srcPath = StringUtils.replace(srcPath, "//", "/");
		// srcPath = StringUtils.replace(srcPath, "//", "/");
		srcPath = FilenameUtils.normalize(srcPath);
		return srcPath;
	}

	public String uncapitalize(String string) {
		return StringUtils.uncapitalize(string);
	}

	protected void warn(String message) {
		OutputUtil.err("[WARNING] " + message);
	}

	protected boolean isIgnoreMethodName(Method method) {
		return contains(method.getName(), ignoreMethodNames);
	}

	protected boolean isRemotingMapped(AccessibleObject accessibleObject) {
		if (accessibleObject.isAnnotationPresent(Remoting.class)) {
			Remoting remotingDestination = accessibleObject.getAnnotation(Remoting.class);
			boolean value = remotingDestination.value();
			if (value) {
				return true;
			}
		} else {
			return true;
		}
		return false;
	}

	protected boolean isRemotingMapped(Class<?> c) {
		if (c.isAnnotationPresent(Remoting.class)) {
			Remoting remotingDestination = c.getAnnotation(Remoting.class);
			boolean value = remotingDestination.value();
			if (value) {
				return true;
			}
		} else {
			return true;
		}
		return false;
	}

	/**
	 * @return the projectName
	 */
	public String getProjectName() {
		return projectName;
	}

	/**
	 * @param projectName
	 *            the projectName to set
	 */
	public void setProjectName(String projectName) {
		this.projectName = projectName;
	}

	protected void sort(List<Class<?>> classes) {
		if (null != classes) {
			Collections.sort(classes, new Comparator<Class<?>>() {

				public int compare(Class<?> o1, Class<?> o2) {
					int compare = o1.getPackage().getName().compareTo(o2.getPackage().getName());
					if (0 == compare) {
						return o1.getSimpleName().compareTo(o2.getSimpleName());
					}
					return compare;
				}

			});
		}
	}

	protected String getTemplateName() {
		return this.getClass().getSimpleName() + ".template.ftl";
	}

	public boolean generate() {
		boolean compliant = isCompliant();
		if (compliant) {

			this.prepare();
			this.optimize();
			this.write();
		}
		return compliant;
	}

	public String getFieldValue(Field field) {
		Class<?> type = field.getType();
		String typeName = type.getName();

		try {
			if (field.getType().isArray()) {
				if (typeName.startsWith(ActionScriptTypeConverter.NON_PRIMITIVE_ARRAY_PREFIX) && typeName.endsWith(";")) {
					String elementName = typeName.substring(ActionScriptTypeConverter.NON_PRIMITIVE_ARRAY_PREFIX.length(), typeName.length() - 1);
					if ("java.lang.String".equals(elementName)) {

						// method = clazz.getMethod(accesser + StringUtils.capitalize(field.getName()), new
						// Class<?>[0]);
						// TODO use Object returnValue = method.invoke(enumClass, (Object[]) null);
						String[] values = (String[]) field.get(null);

						// new Array("one", "two", "three");
						return "new Array(\"" + StringUtils.join(values, "\", \"") + "\")";
					}
				}
			} else if (type.equals(String.class)) {
				return "\"" + String.valueOf(field.get(null)) + "\"";
			}

			return String.valueOf(field.get(null));

		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	@Deprecated
	/* FileUtil.write */
	protected void write() {
		try {
			File filePath = new File(this.destinationPath);
			if (!filePath.exists()) {
				FileUtils.forceMkdir(filePath);
			}
			File file = new File(destinationPath, destinationFileName);
			if (null != file) {
				file.createNewFile();
			}
			/** 只读不能修改 */
			file.setWritable(true);

			BufferedWriter bw = new BufferedWriter(new FileWriter(file));
			bw.write(FreeMarkerUtil.processFile(AbstractActionScriptTypeMapper.class, getTemplateName(), this));
			bw.close();
			filePath.exists();

		} catch (Exception e) {
			System.out.println("this.destinationPath=" + this.destinationPath + ", destinationFileName=" + destinationFileName);
			e.printStackTrace();
		}
	}

	public boolean packageNameStartsWith(String packageName, String[] packagePrefixs) {

		for (String packagePrefix : packagePrefixs) {
			boolean result = packageName.startsWith(packagePrefix);
			if (true == result) {
				return true;
			}
		}

		return false;
	}

	public String getActionScriptTypeName(Class<?> clazz) {

		if (null != clazz && java.util.Collection.class.isAssignableFrom(clazz)) {
			return "mx.collections.ArrayCollection";
		}

		if (Modifier.isInterface(clazz.getModifiers()) && !packageNameStartsWith(clazz.getPackage().getName(), actionScriptMappingContext.getPackagePrefixs())) {
			return "Object";
		}

		ActionScriptTypeDefinition actionScriptTypeDefinition = ActionScriptTypeConverter.get(clazz);
		if (null != actionScriptTypeDefinition) {
			return actionScriptTypeDefinition.getActionScriptType();
		}

		if (clazz.isArray()) {
			return "Array";
		}

		String actionScriptTypeName = clazz.getName();
		if (actionScriptTypeName.startsWith(ActionScriptTypeConverter.NON_PRIMITIVE_ARRAY_PREFIX) && actionScriptTypeName.endsWith(";")) {
			return "Array";
		}

		if (null != clazz && null != clazz.getPackage() && !packageNameStartsWith(clazz.getPackage().getName(), actionScriptMappingContext.getPackagePrefixs())) {
			return "Object";
		}

		return actionScriptTypeName;

	}

	public String getActionScriptTypeDefaultValue(Class<?> clazz) {

		ActionScriptTypeDefinition actionScriptTypeDefinition = ActionScriptTypeConverter.get(clazz);
		if (null != actionScriptTypeDefinition) {
			return actionScriptTypeDefinition.getActionScriptDefaultValue();
		}
		return "null";

	}

	public XClass getXclass(String[] projectNames, Class<?> c) {

		File sourceFile = getSourceFile(projectNames, c);
		if (null != sourceFile) {
			SourceSet sourceSet = new FileSourceSet(sourceFile);
			XJavaDoc javaDoc = new XJavaDoc();
			javaDoc.addSourceSet(sourceSet);
			return javaDoc.getXClass(c.getSimpleName());

		}
		return null;
	}

	public File getSourceFile(String[] projectNames, Class<?> c) {

		String className = c.getCanonicalName();
		String fileSeparator = System.getProperty("file.separator");
		if (fileSeparator == null || "".equals(fileSeparator)) {
			fileSeparator = File.separator;
		}
		className = StringUtils.replace(className, ".", fileSeparator);
		int index = className.indexOf('$');
		if (index > 0) {
			className = className.substring(index);
		}
		className = className + ".java";

		if (null != projectNames) {
			for (String projectName : projectNames) {

				String sourcePath = AbstractActionScriptTypeMapper.absoluteSourcePath(this.getActionScriptMappingContext().getJavaWorkspacePath(), projectName);

				StringBuilder sb = new StringBuilder(sourcePath);
				if (sourcePath.endsWith("/")) {
					sb.append(className);
				} else {
					sb.append(File.separatorChar).append(className);
				}
				File file = new File(sb.toString());
				if (file.exists()) {
					return file;
				}
			}

		}

		return null;
	}

	public static String camelProjectName(String projectName) {
		StringBuilder result = new StringBuilder();
		if (projectName != null && projectName.length() > 0) {
			result.append(projectName.substring(0, 1).toUpperCase());
			boolean upperCase = false;
			for (int i = 1; i < projectName.length(); i++) {
				String s = projectName.substring(i, i + 1);
				if (".".equals(s)) {
					upperCase = true;
				} else {
					if (true == upperCase) {
						result.append(s.toUpperCase());
						upperCase = false;
					} else {
						result.append(s);
					}
				}
			}
		}
		return result.toString();
	}

	public static String hyphenProjectName(String projectName) {
		StringBuilder result = new StringBuilder();
		if (projectName != null && projectName.length() > 0) {
			// result.append(projectName.substring(0, 1).toUpperCase());
			for (int i = 0; i < projectName.length(); i++) {
				String s = projectName.substring(i, i + 1);
				if (".".equals(s)) {
					result.append("-");
				} else {
					result.append(s);
				}
			}
		}
		return result.toString();
	}

	/**
	 * @return the actionScriptMappingContext
	 */
	public ActionScriptMappingContext getActionScriptMappingContext() {
		return actionScriptMappingContext;
	}

	/**
	 * @param actionScriptMappingContext
	 *            the actionScriptMappingContext to set
	 */
	public void setActionScriptMappingContext(ActionScriptMappingContext actionScriptMappingContext) {
		this.actionScriptMappingContext = actionScriptMappingContext;
	}

	/**
	 * @return the applicationContext
	 */
	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	/**
	 * @param applicationContext
	 *            the applicationContext to set
	 */
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

}
